package creatures;

import java.awt.Color;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import engine.Game;
import engine.Persistent;
import engine.display.Displayable;

/**
 * The parent class of all Creatures.
 * <p>
 * <b>Important Note:</b> All creatures must have a default constructor. This constructor is called
 * when loading the creature.
 * 
 * @author Kyle_Solo
 */
public abstract class Creature extends Persistent implements Displayable {
	/**
	 * A list of all the creatures that have been constructed. Used in rendering, saving, and the
	 * time system. All creatures are added automatically to this list upon creation.
	 */
	public static List<Creature> creatureList = new ArrayList<Creature>();
	/**
	 * A list of all the creatures limbs (head, arms, legs, etc.)
	 */
	public List<Limb> limbs;
	private static int idMax = 0;

	/**
	 * Gets rid of all the creatures, removing them from the map, but not from the time system.
	 */
	public static void clearCreatures() {
		for (Creature c : creatureList) {
			c.dontPersist();
			Game.display.remove(c);
		}
		creatureList.clear();
	}

	/**
	 * The character representation of this creature.
	 */
	public String character;

	/**
	 * The color of this creature.
	 */
	public Color color;

	/**
	 * A unique number assigned to this creature. No other creature will have the same id as this
	 * one. Id's do not remain the save across saves.
	 */
	public final int ID;

	/**
	 * Lower speed = faster movement. Defaults to 1.
	 */
	public int speed = 1;

	/**
	 * todo: Refine the partially implemented tag system, or get rid of it. Move creature
	 * definitions to external files, implement a way of getting attributes from a creature.
	 */
	private Set<String> tags = new HashSet<String>();

	/**
	 * The x-coordinate of this creature in cells. It is safe to move creatures by setting their x
	 * and y coordinates directly.
	 */
	public int x;

	/**
	 * The y-coordinate of this creature in cells. It is safe to move creatures by setting their x
	 * and y coordinates directly.
	 */
	public int y;

	/**
	 * Constructs a new Creature
	 */
	public Creature() {
		// add this creature to the time system
		Game.time.add(this);

		// assign an ID
		ID = Creature.idMax++;

		// add to the creature list
		Creature.creatureList.add(this);

		// add to the display
		Game.display.add(this);

		// print out a message with this creature's name
		System.out.println(getClass().getSimpleName() + " " + ID);
	}

	/**
	 * Adds a tag to this creature
	 * 
	 * @param tag
	 *            the tag to add
	 */
	public final void addTag(String tag) {
		tags.add(tag);
	}

	/**
	 * @return the object's speed
	 */
	public final int getSpeed() {
		return speed;
	}

	/**
	 * @param tag
	 *            the tag to search for
	 * @return whether or not this creature has the specified tag
	 */
	public final boolean hasTag(String tag) {
		return tags.contains(tag);
	}

	@Override
	public boolean load(Scanner data) {
		Game.time.set(this, data.nextLong());
		return true;
	}

	/**
	 * Objects should only render if the player can see them
	 */
	public void render() {
		if (Game.map.isVisible(x, y)) {
			Game.display.drawCharacter(x, y, character, color);
		}
	}

	@Override
	public boolean save(PrintStream data) {
		data.append(Game.time.get(this) + " ");
		return true;
	}

	/**
	 * Returns the creature's class name and ID as a string in the form:<br>
	 * <code>[SimpleClassName]_[ID]<code><br>
	 * Used for debugging only.
	 */
	@Override
	public String toString() {
		return this.getClass().getSimpleName() + "_" + ID;
	}

	/**
	 * This method is called when it is this Creature's turn.
	 * 
	 * @return how long this update took in time units
	 */
	public abstract int update();
}
